////////////////////////////////////////////////////////////////////////////
//  Copyright 2009 - 2011 Zach Snow                                       //
////////////////////////////////////////////////////////////////////////////
//  This file is part of WindowsWhere.                                    //
//                                                                        //
//  WindowsWhere is free software: you can redistribute it and/or modify  //
//  it under the terms of the GNU General Public License as published by  //
//  the Free Software Foundation, either version 3 of the License, or     //
//  (at your option) any later version.                                   //
//                                                                        //
//  WindowsWhere is distributed in the hope that it will be useful,       //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of        //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
//  GNU General Public License for more details.                          //
//                                                                        //
//  You should have received a copy of the GNU General Public License     //
//  along with WindowsWhere.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////
// This file was derived from Window.cs, by Chris Wilson                  //
////////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Generic;
using System.Drawing;

namespace WindowsWhere
{

  /// <summary>
  /// This class wraps a window handle and provides access to relevant
  /// window properties.  Subclasses can override OnStartManaging and
  /// OnStopManaging to modify properties of managed windows.
  /// </summary>
	public class Window
	{
		private bool m_IsManaged;
    public IntPtr Handle
    {
      get;
      private set;
    }

    public string WindowText
    {
      get
      {
        return Win32.GetWindowText(Handle).Trim();
      }
    }
    public string WindowClass
    {
      get
      {
        return Win32.GetWindowClass(Handle);
      }
    }

    public bool IsVisible
    {
      get
      {
        return Win32.IsWindowVisible(Handle);
      }
    }

    public IntPtr Owner
    {
      get
      {
        return Win32.GetWindow(Handle, Win32.GW_OWNER);
      }
    }

    public Rectangle Rectangle
    {
      get
      {
        return Win32.GetWindowRectangle(Handle);
      }
      set
      {
        Win32.SetWindowRectangle(Handle, value);
      }
    }


		public Window(IntPtr handle)
		{
			Handle = handle;
			m_IsManaged = true;
      OnStartManaging();
		}

		~Window()
		{
			StopManaging();
		}

		public void StopManaging()
		{
			if(m_IsManaged)
			{
        OnStopManaging();
			}
			m_IsManaged = false;
		}

    public void Activate()
    {
      Win32.ShowWindow(Handle, Win32.SW_RESTORE);
    }

		public override string ToString()
		{
			return WindowText;
		}

    protected virtual void OnStartManaging()
    {
      return;
    }
    protected virtual void OnStopManaging()
    {
      return;
    }
	}

  public class EnumWindowList : List<Window>
  {
    private int m_EnumComplete = 0;
    private bool m_FilterBlanks;
    private bool m_FilterHidden;
    private bool m_FilterChildren;
    private string[] m_FilterStrings;

    public EnumWindowList(bool filterBlanks, bool filterHidden, bool filterChildren, string[] filterStrings)
    {
      // Creating a new instance of this class returns an ArrayList of Window objects, with one for
      // each window matching the search criteria. I think there must be a better way to create code
      // like this than what I've implemented here -- since EnumWindows is asynchronous, we wait until
      // our callback begins receiving the second round of windows, and then know (or hope) that the
      // callback has been called once for each window.
      m_FilterBlanks = filterBlanks;
      m_FilterHidden = filterHidden;
      m_FilterChildren = filterChildren;
      m_FilterStrings = filterStrings;

      Win32.EnumWindows(new Win32.EnumWindowDelegate(EnumWindowCallBack), 0);
      Win32.EnumWindows(new Win32.EnumWindowDelegate(EnumWindowCallBack), 1);

      while (m_EnumComplete == 0)
      {
        System.Windows.Forms.Application.DoEvents();
      }
    }

    private bool EnumWindowCallBack(IntPtr hwnd, int lParam)
    {
      if (lParam == 0)
      {
        var window = new Window(hwnd);
        bool add = true;

        if (m_FilterBlanks && window.WindowText == "")
          add = false;
        if (m_FilterHidden && !window.IsVisible)
          add = false;
        if (m_FilterChildren && window.Owner != IntPtr.Zero)
          add = false;
        foreach (string s in m_FilterStrings)
        {
          if (window.WindowText == s)
            add = false;
        }

        if (add)
          Add(window);
      }

      m_EnumComplete = lParam;
      return true;
    }
  }
}
